{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Environment Setup"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Import packages from other direction. Itis necessary if the project is structured as:\n",
    "# my_project\n",
    "# ├── notebooks\n",
    "# │   └── Generate Radio Data for tr.ipynb\n",
    "# ├── local_python_package\n",
    "# │   ├── __init__.py\n",
    "# │   ├── models.py\n",
    "# ├── README.md\n",
    "import os\n",
    "import sys\n",
    "module_path = os.path.abspath(os.path.join('..'))\n",
    "if module_path not in sys.path:\n",
    "    sys.path.append(module_path)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Using TensorFlow backend.\n"
     ]
    }
   ],
   "source": [
    "import time\n",
    "import multiprocessing as mp\n",
    "import numpy as np\n",
    "\n",
    "from radioml.models import Baseline\n",
    "from radioml.metrics import get_ber_bler\n",
    "from radioml.dataset import RadioDataGenerator\n",
    "\n",
    "from IPython.display import SVG, display\n",
    "from keras.utils.vis_utils import model_to_dot\n",
    "import matplotlib.pyplot as plt\n",
    "%matplotlib inline"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Parameters"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "DATA_LEN = 200\n",
    "PREAMBLE_LEN = 40\n",
    "CHANNEL_LEN = 2\n",
    "\n",
    "SNR_TRAIN = 20.0\n",
    "OMEGA_TRAIN = 1/50"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Create a training generator / validation generator "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "radio = RadioDataGenerator(DATA_LEN,PREAMBLE_LEN, CHANNEL_LEN, modulation_scheme='QPSK')\n",
    "\n",
    "training_generator   = radio.end2end_data_generator(OMEGA_TRAIN, \n",
    "                                                    SNR_TRAIN, \n",
    "                                                    batch_size=128, \n",
    "                                                    num_cpus=8)\n",
    "validation_generator = radio.end2end_data_generator(OMEGA_TRAIN, \n",
    "                                                    SNR_TRAIN, \n",
    "                                                    batch_size=128, \n",
    "                                                    seed=2018)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Modular Network Funcs (CFO, Equalization, Demod & ECC)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import tensorflow as tf\n",
    "from tensorflow.keras.layers import Input, Dense, GRU, LSTM, RepeatVector\n",
    "from tensorflow.keras.layers import Bidirectional, TimeDistributed, BatchNormalization\n",
    "from tensorflow.keras.layers import Lambda\n",
    "\n",
    "tf.keras.backend.clear_session()\n",
    "\n",
    "def cfo_network(preamble, preamble_conv, scope='CFONet'):\n",
    "    \"\"\"\n",
    "    Arguments:\n",
    "        preamble :     tf.Tensor float32 -  [batch, preamble_length, 2]\n",
    "        preamble_conv: tf.Tensor float32 -  [batch, preamble_length, 2]\n",
    "        \n",
    "    Return:\n",
    "        cfo_estimate: tf.Tensor float32 - [batch_size, 1]\n",
    "    \"\"\"\n",
    "    with tf.name_scope(scope):\n",
    "        inputs = tf.keras.layers.concatenate([preamble, preamble_conv], axis=1)\n",
    "        inputs = tf.keras.layers.Flatten(name='Flatten')(inputs)\n",
    "        x = tf.keras.layers.Dense(100, 'tanh', name=scope+\"_dense_1\")(inputs)\n",
    "        x = tf.keras.layers.Dense(100, 'tanh', name=scope+\"_dense_2\")(x)\n",
    "        x = tf.keras.layers.Dense(100, 'tanh', name=scope+\"_dense_3\")(x)\n",
    "    cfo_est = tf.keras.layers.Dense(1, 'linear',name='CFOEstimate')(x)\n",
    "    \n",
    "    return cfo_est\n",
    "\n",
    "def cfo_correction_func(kwargs):\n",
    "    \"\"\"Rotate packet given an omega estimate \n",
    "    \n",
    "    Arguments:\n",
    "    ----------\n",
    "        omega_estimate: tf.Tensor float32 - [batch, 1]\n",
    "        packets:        tf.Tensor float32 - [batch, (preamble_len + data_len), 2] \n",
    "    Returns:\n",
    "    --------\n",
    "        cfo_corrected_packets: tf.Tensor float32 - [batch, (preamble_len + data_len), 2] \n",
    "    \"\"\" \n",
    "    # Because of Lambda Layer, we need to pass arguments as Kwargs\n",
    "    omega_estimate, packets = kwargs[0], kwargs[1]\n",
    "    \n",
    "    with tf.name_scope('rotation_matrix'):\n",
    "        time_step_matrix= tf.range(tf.cast(tf.shape(packets)[1], dtype=tf.float32))\n",
    "        power_term      = tf.complex(0.0, - omega_estimate * time_step_matrix)\n",
    "        rotation_matrix = tf.exp(power_term)\n",
    "    \n",
    "    with tf.name_scope('cfo_correction'):\n",
    "        rotated_packets = tf.complex(packets[..., 0], \n",
    "                                     packets[...,1]) * rotation_matrix\n",
    "        \n",
    "    cfo_corrected_packet = tf.stack([tf.real(rotated_packets), \n",
    "                                     tf.imag(rotated_packets)], \n",
    "                                    axis=-1)\n",
    "    return cfo_corrected_packet\n",
    "\n",
    "def channel_estimation_network(preamble, cfo_corrected_preamble, scope='ChannelEstimationNet'):\n",
    "    \"\"\"Channel Estimation.\"\"\"\n",
    "    with tf.name_scope('ChannelEstimationNet'):\n",
    "        inputs = tf.keras.layers.concatenate([preamble, cfo_corrected_preamble], axis=-1,\n",
    "                                            name='Preamble_CFOCorrectedPreamble')\n",
    "        inputs = tf.keras.layers.Flatten()(inputs)\n",
    "        x = Dense(300, 'relu', name='ChannelEstimationNet_Dense_1')(inputs)\n",
    "        x = Dense(300, 'relu', name='ChannelEstimationNet_Dense_2')(x)\n",
    "        x = Dense(CHANNEL_LEN, 'sigmoid', name='ChannelEstimate')(x)\n",
    "    return x\n",
    "\n",
    "\n",
    "def equalization_network(cfo_corrected_data, chan_est):\n",
    "    \"\"\"Given a [preamble, preambe_conv, data_conv] Predict equalized packet \"\"\"\n",
    "    \n",
    "    chan_est = RepeatVector(DATA_LEN)(chan_est)\n",
    "    inputs = tf.keras.layers.concatenate([cfo_corrected_data, chan_est], axis=-1,\n",
    "                                        name=\"CFOCorrectedData_ChannelEstimate\")\n",
    "    with tf.name_scope('EqualizationNet'):\n",
    "        scope='Equalizationnet'\n",
    "        x = Bidirectional(LSTM(45, return_sequences=True), \n",
    "                          name=scope+'_BiLSTM_1')(inputs)\n",
    "        x = Bidirectional(LSTM(45, return_sequences=True), \n",
    "                          name=scope+'_BiLSTM_2')(x)\n",
    "        x = TimeDistributed(Dense(100, activation='relu'), \n",
    "                            name=scope+'_Dense_1')(x)\n",
    "    x = TimeDistributed(Dense(2, activation='linear'), \n",
    "                        name='Equalized_Packet')(x)\n",
    "    return x\n",
    "\n",
    "def demod_and_ecc_network(equalized_packets):\n",
    "    x = Bidirectional(GRU(400, return_sequences=True))(equalized_packets)\n",
    "    x = BatchNormalization()(x)\n",
    "    x = Bidirectional(GRU(400, return_sequences=True))(x)\n",
    "    x = BatchNormalization()(x)\n",
    "    est_message_bits = TimeDistributed(Dense(1, activation='sigmoid'))(x)\n",
    "    return est_message_bits"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##  Combine into a  Gigantic End-to-End Model!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": false
   },
   "outputs": [],
   "source": [
    "preamble          = Input(shape=(40, 2), name='preamble')\n",
    "corrupted_packets = Input(shape=(240, 2), name='corrupted_packet')  # [preamble_conv, data_conv]\n",
    "\n",
    "preamble_conv = Lambda(lambda x: x[:,:PREAMBLE_LEN, :], \n",
    "                       name='preamble_conv')(corrupted_packets)\n",
    "cfo_est = cfo_network(preamble, preamble_conv)\n",
    "with tf.name_scope('CFO_Correction'):\n",
    "    cfo_corrected_packet  = Lambda(cfo_correction_func, \n",
    "                                    name='CFOCorrection')([cfo_est, corrupted_packets])\n",
    "    # Split packet into preamble, data\n",
    "    cfo_corrected_preamble = Lambda(lambda x: x[:,:PREAMBLE_LEN, :], \n",
    "                                    name='CFOCorrected_Preamble')(cfo_corrected_packet)\n",
    "    cfo_corrected_data    =  Lambda(lambda x: x[:,PREAMBLE_LEN:, :], \n",
    "                                    name='CFOCorrected_Data')(cfo_corrected_packet)\n",
    "    \n",
    "channel_estimate  = channel_estimation_network(preamble, cfo_corrected_preamble)\n",
    "equalized_packet  = equalization_network(cfo_corrected_data, channel_est)\n",
    "data_estimate     = demod_and_ecc_network(equalized_packets)\n",
    "\n",
    "model = tf.keras.Model(inputs=[preamble, corrupted_packets], \n",
    "                       outputs=[data_estimates, cfo_est, channel_est] )\n",
    "print(\"Number of training parameters: %d\" % model.count_params())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "scrolled": false
   },
   "outputs": [
    {
     "data": {
      "image/svg+xml": [
       "<svg height=\"2130pt\" viewBox=\"0.00 0.00 875.50 2130.00\" width=\"876pt\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n",
       "<g class=\"graph\" id=\"graph0\" transform=\"scale(1 1) rotate(0) translate(4 2126)\">\n",
       "<title>G</title>\n",
       "<polygon fill=\"white\" points=\"-4,4 -4,-2126 871.5,-2126 871.5,4 -4,4\" stroke=\"none\"/>\n",
       "<!-- 139805320405288 -->\n",
       "<g class=\"node\" id=\"node1\"><title>139805320405288</title>\n",
       "<polygon fill=\"none\" points=\"42.5,-2075.5 42.5,-2121.5 372.5,-2121.5 372.5,-2075.5 42.5,-2075.5\" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"131.5\" y=\"-2094.8\">corrupted_packet: InputLayer</text>\n",
       "<polyline fill=\"none\" points=\"220.5,-2075.5 220.5,-2121.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"248\" y=\"-2106.3\">input:</text>\n",
       "<polyline fill=\"none\" points=\"220.5,-2098.5 275.5,-2098.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"248\" y=\"-2083.3\">output:</text>\n",
       "<polyline fill=\"none\" points=\"275.5,-2075.5 275.5,-2121.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"324\" y=\"-2106.3\">(None, 240, 2)</text>\n",
       "<polyline fill=\"none\" points=\"275.5,-2098.5 372.5,-2098.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"324\" y=\"-2083.3\">(None, 240, 2)</text>\n",
       "</g>\n",
       "<!-- 139805320406016 -->\n",
       "<g class=\"node\" id=\"node3\"><title>139805320406016</title>\n",
       "<polygon fill=\"none\" points=\"146,-1992.5 146,-2038.5 451,-2038.5 451,-1992.5 146,-1992.5\" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"222.5\" y=\"-2011.8\">preamble_conv: Lambda</text>\n",
       "<polyline fill=\"none\" points=\"299,-1992.5 299,-2038.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"326.5\" y=\"-2023.3\">input:</text>\n",
       "<polyline fill=\"none\" points=\"299,-2015.5 354,-2015.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"326.5\" y=\"-2000.3\">output:</text>\n",
       "<polyline fill=\"none\" points=\"354,-1992.5 354,-2038.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"402.5\" y=\"-2023.3\">(None, 240, 2)</text>\n",
       "<polyline fill=\"none\" points=\"354,-2015.5 451,-2015.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"402.5\" y=\"-2000.3\">(None, 40, 2)</text>\n",
       "</g>\n",
       "<!-- 139805320405288&#45;&gt;139805320406016 -->\n",
       "<g class=\"edge\" id=\"edge1\"><title>139805320405288-&gt;139805320406016</title>\n",
       "<path d=\"M232.366,-2075.37C242.799,-2066.08 255.068,-2055.16 266.187,-2045.26\" fill=\"none\" stroke=\"black\"/>\n",
       "<polygon fill=\"black\" points=\"268.521,-2047.87 273.664,-2038.61 263.867,-2042.64 268.521,-2047.87\" stroke=\"black\"/>\n",
       "</g>\n",
       "<!-- 139805319827184 -->\n",
       "<g class=\"node\" id=\"node10\"><title>139805319827184</title>\n",
       "<polygon fill=\"none\" points=\"47.5,-1411.5 47.5,-1457.5 421.5,-1457.5 421.5,-1411.5 47.5,-1411.5\" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"124\" y=\"-1430.8\">CFOCorrection: Lambda</text>\n",
       "<polyline fill=\"none\" points=\"200.5,-1411.5 200.5,-1457.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"228\" y=\"-1442.3\">input:</text>\n",
       "<polyline fill=\"none\" points=\"200.5,-1434.5 255.5,-1434.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"228\" y=\"-1419.3\">output:</text>\n",
       "<polyline fill=\"none\" points=\"255.5,-1411.5 255.5,-1457.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"338.5\" y=\"-1442.3\">[(None, 1), (None, 240, 2)]</text>\n",
       "<polyline fill=\"none\" points=\"255.5,-1434.5 421.5,-1434.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"338.5\" y=\"-1419.3\">(None, 240, 2)</text>\n",
       "</g>\n",
       "<!-- 139805320405288&#45;&gt;139805319827184 -->\n",
       "<g class=\"edge\" id=\"edge10\"><title>139805320405288-&gt;139805319827184</title>\n",
       "<path d=\"M171.012,-2075.28C158.188,-2065.67 144.896,-2053.33 136.5,-2039 112.419,-1997.89 117.5,-1981.14 117.5,-1933.5 117.5,-1933.5 117.5,-1933.5 117.5,-1599.5 117.5,-1551.1 116.28,-1533.32 144.5,-1494 153.24,-1481.82 165.373,-1471.52 177.904,-1463.12\" fill=\"none\" stroke=\"black\"/>\n",
       "<polygon fill=\"black\" points=\"180.031,-1465.92 186.606,-1457.61 176.286,-1460 180.031,-1465.92\" stroke=\"black\"/>\n",
       "</g>\n",
       "<!-- 139805320405232 -->\n",
       "<g class=\"node\" id=\"node2\"><title>139805320405232</title>\n",
       "<polygon fill=\"none\" points=\"469.5,-1992.5 469.5,-2038.5 749.5,-2038.5 749.5,-1992.5 469.5,-1992.5\" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"537\" y=\"-2011.8\">preamble: InputLayer</text>\n",
       "<polyline fill=\"none\" points=\"604.5,-1992.5 604.5,-2038.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"632\" y=\"-2023.3\">input:</text>\n",
       "<polyline fill=\"none\" points=\"604.5,-2015.5 659.5,-2015.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"632\" y=\"-2000.3\">output:</text>\n",
       "<polyline fill=\"none\" points=\"659.5,-1992.5 659.5,-2038.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"704.5\" y=\"-2023.3\">(None, 40, 2)</text>\n",
       "<polyline fill=\"none\" points=\"659.5,-2015.5 749.5,-2015.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"704.5\" y=\"-2000.3\">(None, 40, 2)</text>\n",
       "</g>\n",
       "<!-- 139805320406688 -->\n",
       "<g class=\"node\" id=\"node4\"><title>139805320406688</title>\n",
       "<polygon fill=\"none\" points=\"145.5,-1909.5 145.5,-1955.5 535.5,-1955.5 535.5,-1909.5 145.5,-1909.5\" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"223\" y=\"-1928.8\">concatenate: Concatenate</text>\n",
       "<polyline fill=\"none\" points=\"300.5,-1909.5 300.5,-1955.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"328\" y=\"-1940.3\">input:</text>\n",
       "<polyline fill=\"none\" points=\"300.5,-1932.5 355.5,-1932.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"328\" y=\"-1917.3\">output:</text>\n",
       "<polyline fill=\"none\" points=\"355.5,-1909.5 355.5,-1955.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"445.5\" y=\"-1940.3\">[(None, 40, 2), (None, 40, 2)]</text>\n",
       "<polyline fill=\"none\" points=\"355.5,-1932.5 535.5,-1932.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"445.5\" y=\"-1917.3\">(None, 80, 2)</text>\n",
       "</g>\n",
       "<!-- 139805320405232&#45;&gt;139805320406688 -->\n",
       "<g class=\"edge\" id=\"edge2\"><title>139805320405232-&gt;139805320406688</title>\n",
       "<path d=\"M536.35,-1992.47C501.509,-1981.98 459.678,-1969.39 423.637,-1958.53\" fill=\"none\" stroke=\"black\"/>\n",
       "<polygon fill=\"black\" points=\"424.22,-1955.05 413.636,-1955.52 422.202,-1961.76 424.22,-1955.05\" stroke=\"black\"/>\n",
       "</g>\n",
       "<!-- 139805319705600 -->\n",
       "<g class=\"node\" id=\"node12\"><title>139805319705600</title>\n",
       "<polygon fill=\"none\" points=\"351.5,-1245.5 351.5,-1291.5 867.5,-1291.5 867.5,-1245.5 351.5,-1245.5\" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"492\" y=\"-1264.8\">Preamble_CFOCorrectedPreamble: Concatenate</text>\n",
       "<polyline fill=\"none\" points=\"632.5,-1245.5 632.5,-1291.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"660\" y=\"-1276.3\">input:</text>\n",
       "<polyline fill=\"none\" points=\"632.5,-1268.5 687.5,-1268.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"660\" y=\"-1253.3\">output:</text>\n",
       "<polyline fill=\"none\" points=\"687.5,-1245.5 687.5,-1291.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"777.5\" y=\"-1276.3\">[(None, 40, 2), (None, 40, 2)]</text>\n",
       "<polyline fill=\"none\" points=\"687.5,-1268.5 867.5,-1268.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"777.5\" y=\"-1253.3\">(None, 40, 4)</text>\n",
       "</g>\n",
       "<!-- 139805320405232&#45;&gt;139805319705600 -->\n",
       "<g class=\"edge\" id=\"edge12\"><title>139805320405232-&gt;139805319705600</title>\n",
       "<path d=\"M609.5,-1992.48C609.5,-1961.01 609.5,-1901.32 609.5,-1850.5 609.5,-1850.5 609.5,-1850.5 609.5,-1433.5 609.5,-1387.84 609.5,-1335.03 609.5,-1301.94\" fill=\"none\" stroke=\"black\"/>\n",
       "<polygon fill=\"black\" points=\"613,-1301.52 609.5,-1291.52 606,-1301.52 613,-1301.52\" stroke=\"black\"/>\n",
       "</g>\n",
       "<!-- 139805320406016&#45;&gt;139805320406688 -->\n",
       "<g class=\"edge\" id=\"edge3\"><title>139805320406016-&gt;139805320406688</title>\n",
       "<path d=\"M309.977,-1992.37C314.421,-1983.79 319.588,-1973.83 324.393,-1964.56\" fill=\"none\" stroke=\"black\"/>\n",
       "<polygon fill=\"black\" points=\"327.541,-1966.1 329.037,-1955.61 321.327,-1962.87 327.541,-1966.1\" stroke=\"black\"/>\n",
       "</g>\n",
       "<!-- 139805320407528 -->\n",
       "<g class=\"node\" id=\"node5\"><title>139805320407528</title>\n",
       "<polygon fill=\"none\" points=\"215.5,-1826.5 215.5,-1872.5 459.5,-1872.5 459.5,-1826.5 215.5,-1826.5\" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"265\" y=\"-1845.8\">Flatten: Flatten</text>\n",
       "<polyline fill=\"none\" points=\"314.5,-1826.5 314.5,-1872.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"342\" y=\"-1857.3\">input:</text>\n",
       "<polyline fill=\"none\" points=\"314.5,-1849.5 369.5,-1849.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"342\" y=\"-1834.3\">output:</text>\n",
       "<polyline fill=\"none\" points=\"369.5,-1826.5 369.5,-1872.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"414.5\" y=\"-1857.3\">(None, 80, 2)</text>\n",
       "<polyline fill=\"none\" points=\"369.5,-1849.5 459.5,-1849.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"414.5\" y=\"-1834.3\">(None, 160)</text>\n",
       "</g>\n",
       "<!-- 139805320406688&#45;&gt;139805320407528 -->\n",
       "<g class=\"edge\" id=\"edge4\"><title>139805320406688-&gt;139805320407528</title>\n",
       "<path d=\"M339.68,-1909.37C339.376,-1901.15 339.024,-1891.66 338.694,-1882.73\" fill=\"none\" stroke=\"black\"/>\n",
       "<polygon fill=\"black\" points=\"342.187,-1882.47 338.319,-1872.61 335.191,-1882.73 342.187,-1882.47\" stroke=\"black\"/>\n",
       "</g>\n",
       "<!-- 139805320406744 -->\n",
       "<g class=\"node\" id=\"node6\"><title>139805320406744</title>\n",
       "<polygon fill=\"none\" points=\"167,-1743.5 167,-1789.5 460,-1789.5 460,-1743.5 167,-1743.5\" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"244.5\" y=\"-1762.8\">CFONet_dense_1: Dense</text>\n",
       "<polyline fill=\"none\" points=\"322,-1743.5 322,-1789.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"349.5\" y=\"-1774.3\">input:</text>\n",
       "<polyline fill=\"none\" points=\"322,-1766.5 377,-1766.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"349.5\" y=\"-1751.3\">output:</text>\n",
       "<polyline fill=\"none\" points=\"377,-1743.5 377,-1789.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"418.5\" y=\"-1774.3\">(None, 160)</text>\n",
       "<polyline fill=\"none\" points=\"377,-1766.5 460,-1766.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"418.5\" y=\"-1751.3\">(None, 100)</text>\n",
       "</g>\n",
       "<!-- 139805320407528&#45;&gt;139805320406744 -->\n",
       "<g class=\"edge\" id=\"edge5\"><title>139805320407528-&gt;139805320406744</title>\n",
       "<path d=\"M330.942,-1826.37C328.481,-1818.06 325.634,-1808.45 322.962,-1799.43\" fill=\"none\" stroke=\"black\"/>\n",
       "<polygon fill=\"black\" points=\"326.247,-1798.2 320.05,-1789.61 319.535,-1800.19 326.247,-1798.2\" stroke=\"black\"/>\n",
       "</g>\n",
       "<!-- 139805320408480 -->\n",
       "<g class=\"node\" id=\"node7\"><title>139805320408480</title>\n",
       "<polygon fill=\"none\" points=\"155,-1660.5 155,-1706.5 448,-1706.5 448,-1660.5 155,-1660.5\" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"232.5\" y=\"-1679.8\">CFONet_dense_2: Dense</text>\n",
       "<polyline fill=\"none\" points=\"310,-1660.5 310,-1706.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"337.5\" y=\"-1691.3\">input:</text>\n",
       "<polyline fill=\"none\" points=\"310,-1683.5 365,-1683.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"337.5\" y=\"-1668.3\">output:</text>\n",
       "<polyline fill=\"none\" points=\"365,-1660.5 365,-1706.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"406.5\" y=\"-1691.3\">(None, 100)</text>\n",
       "<polyline fill=\"none\" points=\"365,-1683.5 448,-1683.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"406.5\" y=\"-1668.3\">(None, 100)</text>\n",
       "</g>\n",
       "<!-- 139805320406744&#45;&gt;139805320408480 -->\n",
       "<g class=\"edge\" id=\"edge6\"><title>139805320406744-&gt;139805320408480</title>\n",
       "<path d=\"M310.221,-1743.37C309.004,-1735.15 307.597,-1725.66 306.274,-1716.73\" fill=\"none\" stroke=\"black\"/>\n",
       "<polygon fill=\"black\" points=\"309.703,-1715.99 304.775,-1706.61 302.778,-1717.01 309.703,-1715.99\" stroke=\"black\"/>\n",
       "</g>\n",
       "<!-- 139805319953096 -->\n",
       "<g class=\"node\" id=\"node8\"><title>139805319953096</title>\n",
       "<polygon fill=\"none\" points=\"149,-1577.5 149,-1623.5 442,-1623.5 442,-1577.5 149,-1577.5\" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"226.5\" y=\"-1596.8\">CFONet_dense_3: Dense</text>\n",
       "<polyline fill=\"none\" points=\"304,-1577.5 304,-1623.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"331.5\" y=\"-1608.3\">input:</text>\n",
       "<polyline fill=\"none\" points=\"304,-1600.5 359,-1600.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"331.5\" y=\"-1585.3\">output:</text>\n",
       "<polyline fill=\"none\" points=\"359,-1577.5 359,-1623.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"400.5\" y=\"-1608.3\">(None, 100)</text>\n",
       "<polyline fill=\"none\" points=\"359,-1600.5 442,-1600.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"400.5\" y=\"-1585.3\">(None, 100)</text>\n",
       "</g>\n",
       "<!-- 139805320408480&#45;&gt;139805319953096 -->\n",
       "<g class=\"edge\" id=\"edge7\"><title>139805320408480-&gt;139805319953096</title>\n",
       "<path d=\"M299.86,-1660.37C299.252,-1652.15 298.549,-1642.66 297.887,-1633.73\" fill=\"none\" stroke=\"black\"/>\n",
       "<polygon fill=\"black\" points=\"301.367,-1633.32 297.138,-1623.61 294.386,-1633.84 301.367,-1633.32\" stroke=\"black\"/>\n",
       "</g>\n",
       "<!-- 139808088674032 -->\n",
       "<g class=\"node\" id=\"node9\"><title>139808088674032</title>\n",
       "<polygon fill=\"none\" points=\"153.5,-1494.5 153.5,-1540.5 423.5,-1540.5 423.5,-1494.5 153.5,-1494.5\" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"219.5\" y=\"-1513.8\">CFOEstimate: Dense</text>\n",
       "<polyline fill=\"none\" points=\"285.5,-1494.5 285.5,-1540.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"313\" y=\"-1525.3\">input:</text>\n",
       "<polyline fill=\"none\" points=\"285.5,-1517.5 340.5,-1517.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"313\" y=\"-1502.3\">output:</text>\n",
       "<polyline fill=\"none\" points=\"340.5,-1494.5 340.5,-1540.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"382\" y=\"-1525.3\">(None, 100)</text>\n",
       "<polyline fill=\"none\" points=\"340.5,-1517.5 423.5,-1517.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"382\" y=\"-1502.3\">(None, 1)</text>\n",
       "</g>\n",
       "<!-- 139805319953096&#45;&gt;139808088674032 -->\n",
       "<g class=\"edge\" id=\"edge8\"><title>139805319953096-&gt;139808088674032</title>\n",
       "<path d=\"M293.587,-1577.37C292.877,-1569.15 292.057,-1559.66 291.285,-1550.73\" fill=\"none\" stroke=\"black\"/>\n",
       "<polygon fill=\"black\" points=\"294.759,-1550.27 290.41,-1540.61 287.785,-1550.87 294.759,-1550.27\" stroke=\"black\"/>\n",
       "</g>\n",
       "<!-- 139808088674032&#45;&gt;139805319827184 -->\n",
       "<g class=\"edge\" id=\"edge9\"><title>139808088674032-&gt;139805319827184</title>\n",
       "<path d=\"M273.744,-1494.37C267.911,-1485.62 261.109,-1475.41 254.823,-1465.99\" fill=\"none\" stroke=\"black\"/>\n",
       "<polygon fill=\"black\" points=\"257.697,-1463.99 249.238,-1457.61 251.873,-1467.87 257.697,-1463.99\" stroke=\"black\"/>\n",
       "</g>\n",
       "<!-- 139805319426904 -->\n",
       "<g class=\"node\" id=\"node11\"><title>139805319426904</title>\n",
       "<polygon fill=\"none\" points=\"209,-1328.5 209,-1374.5 568,-1374.5 568,-1328.5 209,-1328.5\" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"312.5\" y=\"-1347.8\">CFOCorrected_Preamble: Lambda</text>\n",
       "<polyline fill=\"none\" points=\"416,-1328.5 416,-1374.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"443.5\" y=\"-1359.3\">input:</text>\n",
       "<polyline fill=\"none\" points=\"416,-1351.5 471,-1351.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"443.5\" y=\"-1336.3\">output:</text>\n",
       "<polyline fill=\"none\" points=\"471,-1328.5 471,-1374.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"519.5\" y=\"-1359.3\">(None, 240, 2)</text>\n",
       "<polyline fill=\"none\" points=\"471,-1351.5 568,-1351.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"519.5\" y=\"-1336.3\">(None, 40, 2)</text>\n",
       "</g>\n",
       "<!-- 139805319827184&#45;&gt;139805319426904 -->\n",
       "<g class=\"edge\" id=\"edge11\"><title>139805319827184-&gt;139805319426904</title>\n",
       "<path d=\"M276.581,-1411.37C295.425,-1401.46 317.808,-1389.68 337.587,-1379.28\" fill=\"none\" stroke=\"black\"/>\n",
       "<polygon fill=\"black\" points=\"339.249,-1382.36 346.47,-1374.61 335.99,-1376.16 339.249,-1382.36\" stroke=\"black\"/>\n",
       "</g>\n",
       "<!-- 139805319168576 -->\n",
       "<g class=\"node\" id=\"node17\"><title>139805319168576</title>\n",
       "<polygon fill=\"none\" points=\"0,-1245.5 0,-1291.5 333,-1291.5 333,-1245.5 0,-1245.5\" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"90.5\" y=\"-1264.8\">CFOCorrected_Data: Lambda</text>\n",
       "<polyline fill=\"none\" points=\"181,-1245.5 181,-1291.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"208.5\" y=\"-1276.3\">input:</text>\n",
       "<polyline fill=\"none\" points=\"181,-1268.5 236,-1268.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"208.5\" y=\"-1253.3\">output:</text>\n",
       "<polyline fill=\"none\" points=\"236,-1245.5 236,-1291.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"284.5\" y=\"-1276.3\">(None, 240, 2)</text>\n",
       "<polyline fill=\"none\" points=\"236,-1268.5 333,-1268.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"284.5\" y=\"-1253.3\">(None, 200, 2)</text>\n",
       "</g>\n",
       "<!-- 139805319827184&#45;&gt;139805319168576 -->\n",
       "<g class=\"edge\" id=\"edge18\"><title>139805319827184-&gt;139805319168576</title>\n",
       "<path d=\"M219.582,-1411.39C212.825,-1400.72 205.113,-1387.53 199.5,-1375 188.847,-1351.22 180.211,-1322.99 174.449,-1301.71\" fill=\"none\" stroke=\"black\"/>\n",
       "<polygon fill=\"black\" points=\"177.79,-1300.66 171.856,-1291.88 171.022,-1302.44 177.79,-1300.66\" stroke=\"black\"/>\n",
       "</g>\n",
       "<!-- 139805319426904&#45;&gt;139805319705600 -->\n",
       "<g class=\"edge\" id=\"edge13\"><title>139805319426904-&gt;139805319705600</title>\n",
       "<path d=\"M448.597,-1328.47C476.732,-1318.16 510.415,-1305.82 539.675,-1295.09\" fill=\"none\" stroke=\"black\"/>\n",
       "<polygon fill=\"black\" points=\"541.23,-1298.25 549.415,-1291.52 538.821,-1291.68 541.23,-1298.25\" stroke=\"black\"/>\n",
       "</g>\n",
       "<!-- 139805319602808 -->\n",
       "<g class=\"node\" id=\"node13\"><title>139805319602808</title>\n",
       "<polygon fill=\"none\" points=\"474,-1162.5 474,-1208.5 715,-1208.5 715,-1162.5 474,-1162.5\" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"522\" y=\"-1181.8\">flatten: Flatten</text>\n",
       "<polyline fill=\"none\" points=\"570,-1162.5 570,-1208.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"597.5\" y=\"-1193.3\">input:</text>\n",
       "<polyline fill=\"none\" points=\"570,-1185.5 625,-1185.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"597.5\" y=\"-1170.3\">output:</text>\n",
       "<polyline fill=\"none\" points=\"625,-1162.5 625,-1208.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"670\" y=\"-1193.3\">(None, 40, 4)</text>\n",
       "<polyline fill=\"none\" points=\"625,-1185.5 715,-1185.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"670\" y=\"-1170.3\">(None, 160)</text>\n",
       "</g>\n",
       "<!-- 139805319705600&#45;&gt;139805319602808 -->\n",
       "<g class=\"edge\" id=\"edge14\"><title>139805319705600-&gt;139805319602808</title>\n",
       "<path d=\"M605.401,-1245.37C603.88,-1237.15 602.122,-1227.66 600.468,-1218.73\" fill=\"none\" stroke=\"black\"/>\n",
       "<polygon fill=\"black\" points=\"603.856,-1217.8 598.594,-1208.61 596.973,-1219.08 603.856,-1217.8\" stroke=\"black\"/>\n",
       "</g>\n",
       "<!-- 139805308299248 -->\n",
       "<g class=\"node\" id=\"node14\"><title>139805308299248</title>\n",
       "<polygon fill=\"none\" points=\"350.5,-1079.5 350.5,-1125.5 724.5,-1125.5 724.5,-1079.5 350.5,-1079.5\" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"468.5\" y=\"-1098.8\">ChannelEstimationNet_Dense_1: Dense</text>\n",
       "<polyline fill=\"none\" points=\"586.5,-1079.5 586.5,-1125.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"614\" y=\"-1110.3\">input:</text>\n",
       "<polyline fill=\"none\" points=\"586.5,-1102.5 641.5,-1102.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"614\" y=\"-1087.3\">output:</text>\n",
       "<polyline fill=\"none\" points=\"641.5,-1079.5 641.5,-1125.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"683\" y=\"-1110.3\">(None, 160)</text>\n",
       "<polyline fill=\"none\" points=\"641.5,-1102.5 724.5,-1102.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"683\" y=\"-1087.3\">(None, 300)</text>\n",
       "</g>\n",
       "<!-- 139805319602808&#45;&gt;139805308299248 -->\n",
       "<g class=\"edge\" id=\"edge15\"><title>139805319602808-&gt;139805308299248</title>\n",
       "<path d=\"M578.924,-1162.37C572.767,-1153.62 565.588,-1143.41 558.952,-1133.99\" fill=\"none\" stroke=\"black\"/>\n",
       "<polygon fill=\"black\" points=\"561.674,-1131.77 553.057,-1125.61 555.949,-1135.8 561.674,-1131.77\" stroke=\"black\"/>\n",
       "</g>\n",
       "<!-- 139805308361360 -->\n",
       "<g class=\"node\" id=\"node15\"><title>139805308361360</title>\n",
       "<polygon fill=\"none\" points=\"322.5,-996.5 322.5,-1042.5 696.5,-1042.5 696.5,-996.5 322.5,-996.5\" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"440.5\" y=\"-1015.8\">ChannelEstimationNet_Dense_2: Dense</text>\n",
       "<polyline fill=\"none\" points=\"558.5,-996.5 558.5,-1042.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"586\" y=\"-1027.3\">input:</text>\n",
       "<polyline fill=\"none\" points=\"558.5,-1019.5 613.5,-1019.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"586\" y=\"-1004.3\">output:</text>\n",
       "<polyline fill=\"none\" points=\"613.5,-996.5 613.5,-1042.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"655\" y=\"-1027.3\">(None, 300)</text>\n",
       "<polyline fill=\"none\" points=\"613.5,-1019.5 696.5,-1019.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"655\" y=\"-1004.3\">(None, 300)</text>\n",
       "</g>\n",
       "<!-- 139805308299248&#45;&gt;139805308361360 -->\n",
       "<g class=\"edge\" id=\"edge16\"><title>139805308299248-&gt;139805308361360</title>\n",
       "<path d=\"M529.849,-1079.37C526.947,-1070.97 523.585,-1061.24 520.438,-1052.14\" fill=\"none\" stroke=\"black\"/>\n",
       "<polygon fill=\"black\" points=\"523.717,-1050.91 517.142,-1042.61 517.101,-1053.2 523.717,-1050.91\" stroke=\"black\"/>\n",
       "</g>\n",
       "<!-- 139805308362704 -->\n",
       "<g class=\"node\" id=\"node16\"><title>139805308362704</title>\n",
       "<polygon fill=\"none\" points=\"355,-913.5 355,-959.5 644,-959.5 644,-913.5 355,-913.5\" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"430.5\" y=\"-932.8\">ChannelEstimate: Dense</text>\n",
       "<polyline fill=\"none\" points=\"506,-913.5 506,-959.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"533.5\" y=\"-944.3\">input:</text>\n",
       "<polyline fill=\"none\" points=\"506,-936.5 561,-936.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"533.5\" y=\"-921.3\">output:</text>\n",
       "<polyline fill=\"none\" points=\"561,-913.5 561,-959.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"602.5\" y=\"-944.3\">(None, 300)</text>\n",
       "<polyline fill=\"none\" points=\"561,-936.5 644,-936.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"602.5\" y=\"-921.3\">(None, 2)</text>\n",
       "</g>\n",
       "<!-- 139805308361360&#45;&gt;139805308362704 -->\n",
       "<g class=\"edge\" id=\"edge17\"><title>139805308361360-&gt;139805308362704</title>\n",
       "<path d=\"M506.767,-996.366C505.753,-988.152 504.581,-978.658 503.478,-969.725\" fill=\"none\" stroke=\"black\"/>\n",
       "<polygon fill=\"black\" points=\"506.928,-969.103 502.229,-959.607 499.981,-969.96 506.928,-969.103\" stroke=\"black\"/>\n",
       "</g>\n",
       "<!-- 139805319826064 -->\n",
       "<g class=\"node\" id=\"node18\"><title>139805319826064</title>\n",
       "<polygon fill=\"none\" points=\"318.5,-830.5 318.5,-876.5 642.5,-876.5 642.5,-830.5 318.5,-830.5\" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"404.5\" y=\"-849.8\">repeat_vector: RepeatVector</text>\n",
       "<polyline fill=\"none\" points=\"490.5,-830.5 490.5,-876.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"518\" y=\"-861.3\">input:</text>\n",
       "<polyline fill=\"none\" points=\"490.5,-853.5 545.5,-853.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"518\" y=\"-838.3\">output:</text>\n",
       "<polyline fill=\"none\" points=\"545.5,-830.5 545.5,-876.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"594\" y=\"-861.3\">(None, 2)</text>\n",
       "<polyline fill=\"none\" points=\"545.5,-853.5 642.5,-853.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"594\" y=\"-838.3\">(None, 200, 2)</text>\n",
       "</g>\n",
       "<!-- 139805308362704&#45;&gt;139805319826064 -->\n",
       "<g class=\"edge\" id=\"edge19\"><title>139805308362704-&gt;139805319826064</title>\n",
       "<path d=\"M494.308,-913.366C492.36,-905.062 490.106,-895.451 487.991,-886.434\" fill=\"none\" stroke=\"black\"/>\n",
       "<polygon fill=\"black\" points=\"491.377,-885.543 485.686,-876.607 484.562,-887.142 491.377,-885.543\" stroke=\"black\"/>\n",
       "</g>\n",
       "<!-- 139805319706048 -->\n",
       "<g class=\"node\" id=\"node19\"><title>139805319706048</title>\n",
       "<polygon fill=\"none\" points=\"108,-747.5 108,-793.5 653,-793.5 653,-747.5 108,-747.5\" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"256\" y=\"-766.8\">CFOCorrectedData_ChannelEstimate: Concatenate</text>\n",
       "<polyline fill=\"none\" points=\"404,-747.5 404,-793.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"431.5\" y=\"-778.3\">input:</text>\n",
       "<polyline fill=\"none\" points=\"404,-770.5 459,-770.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"431.5\" y=\"-755.3\">output:</text>\n",
       "<polyline fill=\"none\" points=\"459,-747.5 459,-793.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"556\" y=\"-778.3\">[(None, 200, 2), (None, 200, 2)]</text>\n",
       "<polyline fill=\"none\" points=\"459,-770.5 653,-770.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"556\" y=\"-755.3\">(None, 200, 4)</text>\n",
       "</g>\n",
       "<!-- 139805319168576&#45;&gt;139805319706048 -->\n",
       "<g class=\"edge\" id=\"edge20\"><title>139805319168576-&gt;139805319706048</title>\n",
       "<path d=\"M195.267,-1245.38C229.039,-1216.67 280.5,-1163.14 280.5,-1103.5 280.5,-1103.5 280.5,-1103.5 280.5,-935.5 280.5,-886.872 283.057,-870.81 309.5,-830 316.836,-818.678 327.061,-808.459 337.469,-799.847\" fill=\"none\" stroke=\"black\"/>\n",
       "<polygon fill=\"black\" points=\"339.731,-802.521 345.42,-793.583 335.399,-797.022 339.731,-802.521\" stroke=\"black\"/>\n",
       "</g>\n",
       "<!-- 139805319826064&#45;&gt;139805319706048 -->\n",
       "<g class=\"edge\" id=\"edge21\"><title>139805319826064-&gt;139805319706048</title>\n",
       "<path d=\"M453.175,-830.366C441.6,-820.991 427.968,-809.949 415.657,-799.977\" fill=\"none\" stroke=\"black\"/>\n",
       "<polygon fill=\"black\" points=\"417.766,-797.181 407.792,-793.607 413.36,-802.621 417.766,-797.181\" stroke=\"black\"/>\n",
       "</g>\n",
       "<!-- 139805307919328 -->\n",
       "<g class=\"node\" id=\"node20\"><title>139805307919328</title>\n",
       "<polygon fill=\"none\" points=\"176.5,-664.5 176.5,-710.5 584.5,-710.5 584.5,-664.5 176.5,-664.5\" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"301\" y=\"-683.8\">Equalizationnet_BiLSTM_1: Bidirectional</text>\n",
       "<polyline fill=\"none\" points=\"425.5,-664.5 425.5,-710.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"453\" y=\"-695.3\">input:</text>\n",
       "<polyline fill=\"none\" points=\"425.5,-687.5 480.5,-687.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"453\" y=\"-672.3\">output:</text>\n",
       "<polyline fill=\"none\" points=\"480.5,-664.5 480.5,-710.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"532.5\" y=\"-695.3\">(None, 200, 4)</text>\n",
       "<polyline fill=\"none\" points=\"480.5,-687.5 584.5,-687.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"532.5\" y=\"-672.3\">(None, 200, 90)</text>\n",
       "</g>\n",
       "<!-- 139805319706048&#45;&gt;139805307919328 -->\n",
       "<g class=\"edge\" id=\"edge22\"><title>139805319706048-&gt;139805307919328</title>\n",
       "<path d=\"M380.5,-747.366C380.5,-739.152 380.5,-729.658 380.5,-720.725\" fill=\"none\" stroke=\"black\"/>\n",
       "<polygon fill=\"black\" points=\"384,-720.607 380.5,-710.607 377,-720.607 384,-720.607\" stroke=\"black\"/>\n",
       "</g>\n",
       "<!-- 139805301351368 -->\n",
       "<g class=\"node\" id=\"node21\"><title>139805301351368</title>\n",
       "<polygon fill=\"none\" points=\"176.5,-581.5 176.5,-627.5 584.5,-627.5 584.5,-581.5 176.5,-581.5\" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"301\" y=\"-600.8\">Equalizationnet_BiLSTM_2: Bidirectional</text>\n",
       "<polyline fill=\"none\" points=\"425.5,-581.5 425.5,-627.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"453\" y=\"-612.3\">input:</text>\n",
       "<polyline fill=\"none\" points=\"425.5,-604.5 480.5,-604.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"453\" y=\"-589.3\">output:</text>\n",
       "<polyline fill=\"none\" points=\"480.5,-581.5 480.5,-627.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"532.5\" y=\"-612.3\">(None, 200, 90)</text>\n",
       "<polyline fill=\"none\" points=\"480.5,-604.5 584.5,-604.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"532.5\" y=\"-589.3\">(None, 200, 90)</text>\n",
       "</g>\n",
       "<!-- 139805307919328&#45;&gt;139805301351368 -->\n",
       "<g class=\"edge\" id=\"edge23\"><title>139805307919328-&gt;139805301351368</title>\n",
       "<path d=\"M380.5,-664.366C380.5,-656.152 380.5,-646.658 380.5,-637.725\" fill=\"none\" stroke=\"black\"/>\n",
       "<polygon fill=\"black\" points=\"384,-637.607 380.5,-627.607 377,-637.607 384,-637.607\" stroke=\"black\"/>\n",
       "</g>\n",
       "<!-- 139805296173008 -->\n",
       "<g class=\"node\" id=\"node22\"><title>139805296173008</title>\n",
       "<polygon fill=\"none\" points=\"171.5,-498.5 171.5,-544.5 589.5,-544.5 589.5,-498.5 171.5,-498.5\" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"298\" y=\"-517.8\">Equalizationnet_Dense_1: TimeDistributed</text>\n",
       "<polyline fill=\"none\" points=\"424.5,-498.5 424.5,-544.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"452\" y=\"-529.3\">input:</text>\n",
       "<polyline fill=\"none\" points=\"424.5,-521.5 479.5,-521.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"452\" y=\"-506.3\">output:</text>\n",
       "<polyline fill=\"none\" points=\"479.5,-498.5 479.5,-544.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"534.5\" y=\"-529.3\">(None, 200, 90)</text>\n",
       "<polyline fill=\"none\" points=\"479.5,-521.5 589.5,-521.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"534.5\" y=\"-506.3\">(None, 200, 100)</text>\n",
       "</g>\n",
       "<!-- 139805301351368&#45;&gt;139805296173008 -->\n",
       "<g class=\"edge\" id=\"edge24\"><title>139805301351368-&gt;139805296173008</title>\n",
       "<path d=\"M380.5,-581.366C380.5,-573.152 380.5,-563.658 380.5,-554.725\" fill=\"none\" stroke=\"black\"/>\n",
       "<polygon fill=\"black\" points=\"384,-554.607 380.5,-544.607 377,-554.607 384,-554.607\" stroke=\"black\"/>\n",
       "</g>\n",
       "<!-- 139805294616528 -->\n",
       "<g class=\"node\" id=\"node23\"><title>139805294616528</title>\n",
       "<polygon fill=\"none\" points=\"192.5,-415.5 192.5,-461.5 568.5,-461.5 568.5,-415.5 192.5,-415.5\" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"298\" y=\"-434.8\">Equalized_Packet: TimeDistributed</text>\n",
       "<polyline fill=\"none\" points=\"403.5,-415.5 403.5,-461.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"431\" y=\"-446.3\">input:</text>\n",
       "<polyline fill=\"none\" points=\"403.5,-438.5 458.5,-438.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"431\" y=\"-423.3\">output:</text>\n",
       "<polyline fill=\"none\" points=\"458.5,-415.5 458.5,-461.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"513.5\" y=\"-446.3\">(None, 200, 100)</text>\n",
       "<polyline fill=\"none\" points=\"458.5,-438.5 568.5,-438.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"513.5\" y=\"-423.3\">(None, 200, 2)</text>\n",
       "</g>\n",
       "<!-- 139805296173008&#45;&gt;139805294616528 -->\n",
       "<g class=\"edge\" id=\"edge25\"><title>139805296173008-&gt;139805294616528</title>\n",
       "<path d=\"M380.5,-498.366C380.5,-490.152 380.5,-480.658 380.5,-471.725\" fill=\"none\" stroke=\"black\"/>\n",
       "<polygon fill=\"black\" points=\"384,-471.607 380.5,-461.607 377,-471.607 384,-471.607\" stroke=\"black\"/>\n",
       "</g>\n",
       "<!-- 139805294066320 -->\n",
       "<g class=\"node\" id=\"node24\"><title>139805294066320</title>\n",
       "<polygon fill=\"none\" points=\"216.5,-332.5 216.5,-378.5 544.5,-378.5 544.5,-332.5 216.5,-332.5\" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"298\" y=\"-351.8\">bidirectional: Bidirectional</text>\n",
       "<polyline fill=\"none\" points=\"379.5,-332.5 379.5,-378.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"407\" y=\"-363.3\">input:</text>\n",
       "<polyline fill=\"none\" points=\"379.5,-355.5 434.5,-355.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"407\" y=\"-340.3\">output:</text>\n",
       "<polyline fill=\"none\" points=\"434.5,-332.5 434.5,-378.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"489.5\" y=\"-363.3\">(None, 200, 2)</text>\n",
       "<polyline fill=\"none\" points=\"434.5,-355.5 544.5,-355.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"489.5\" y=\"-340.3\">(None, 200, 800)</text>\n",
       "</g>\n",
       "<!-- 139805294616528&#45;&gt;139805294066320 -->\n",
       "<g class=\"edge\" id=\"edge26\"><title>139805294616528-&gt;139805294066320</title>\n",
       "<path d=\"M380.5,-415.366C380.5,-407.152 380.5,-397.658 380.5,-388.725\" fill=\"none\" stroke=\"black\"/>\n",
       "<polygon fill=\"black\" points=\"384,-388.607 380.5,-378.607 377,-388.607 384,-388.607\" stroke=\"black\"/>\n",
       "</g>\n",
       "<!-- 139805294066992 -->\n",
       "<g class=\"node\" id=\"node25\"><title>139805294066992</title>\n",
       "<polygon fill=\"none\" points=\"175,-249.5 175,-295.5 586,-295.5 586,-249.5 175,-249.5\" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"298\" y=\"-268.8\">batch_normalization: BatchNormalization</text>\n",
       "<polyline fill=\"none\" points=\"421,-249.5 421,-295.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"448.5\" y=\"-280.3\">input:</text>\n",
       "<polyline fill=\"none\" points=\"421,-272.5 476,-272.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"448.5\" y=\"-257.3\">output:</text>\n",
       "<polyline fill=\"none\" points=\"476,-249.5 476,-295.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"531\" y=\"-280.3\">(None, 200, 800)</text>\n",
       "<polyline fill=\"none\" points=\"476,-272.5 586,-272.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"531\" y=\"-257.3\">(None, 200, 800)</text>\n",
       "</g>\n",
       "<!-- 139805294066320&#45;&gt;139805294066992 -->\n",
       "<g class=\"edge\" id=\"edge27\"><title>139805294066320-&gt;139805294066992</title>\n",
       "<path d=\"M380.5,-332.366C380.5,-324.152 380.5,-314.658 380.5,-305.725\" fill=\"none\" stroke=\"black\"/>\n",
       "<polygon fill=\"black\" points=\"384,-305.607 380.5,-295.607 377,-305.607 384,-305.607\" stroke=\"black\"/>\n",
       "</g>\n",
       "<!-- 139805288058328 -->\n",
       "<g class=\"node\" id=\"node26\"><title>139805288058328</title>\n",
       "<polygon fill=\"none\" points=\"210,-166.5 210,-212.5 551,-212.5 551,-166.5 210,-166.5\" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"298\" y=\"-185.8\">bidirectional_1: Bidirectional</text>\n",
       "<polyline fill=\"none\" points=\"386,-166.5 386,-212.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"413.5\" y=\"-197.3\">input:</text>\n",
       "<polyline fill=\"none\" points=\"386,-189.5 441,-189.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"413.5\" y=\"-174.3\">output:</text>\n",
       "<polyline fill=\"none\" points=\"441,-166.5 441,-212.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"496\" y=\"-197.3\">(None, 200, 800)</text>\n",
       "<polyline fill=\"none\" points=\"441,-189.5 551,-189.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"496\" y=\"-174.3\">(None, 200, 800)</text>\n",
       "</g>\n",
       "<!-- 139805294066992&#45;&gt;139805288058328 -->\n",
       "<g class=\"edge\" id=\"edge28\"><title>139805294066992-&gt;139805288058328</title>\n",
       "<path d=\"M380.5,-249.366C380.5,-241.152 380.5,-231.658 380.5,-222.725\" fill=\"none\" stroke=\"black\"/>\n",
       "<polygon fill=\"black\" points=\"384,-222.607 380.5,-212.607 377,-222.607 384,-222.607\" stroke=\"black\"/>\n",
       "</g>\n",
       "<!-- 139805288210616 -->\n",
       "<g class=\"node\" id=\"node27\"><title>139805288210616</title>\n",
       "<polygon fill=\"none\" points=\"168,-83.5 168,-129.5 593,-129.5 593,-83.5 168,-83.5\" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"298\" y=\"-102.8\">batch_normalization_1: BatchNormalization</text>\n",
       "<polyline fill=\"none\" points=\"428,-83.5 428,-129.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"455.5\" y=\"-114.3\">input:</text>\n",
       "<polyline fill=\"none\" points=\"428,-106.5 483,-106.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"455.5\" y=\"-91.3\">output:</text>\n",
       "<polyline fill=\"none\" points=\"483,-83.5 483,-129.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"538\" y=\"-114.3\">(None, 200, 800)</text>\n",
       "<polyline fill=\"none\" points=\"483,-106.5 593,-106.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"538\" y=\"-91.3\">(None, 200, 800)</text>\n",
       "</g>\n",
       "<!-- 139805288058328&#45;&gt;139805288210616 -->\n",
       "<g class=\"edge\" id=\"edge29\"><title>139805288058328-&gt;139805288210616</title>\n",
       "<path d=\"M380.5,-166.366C380.5,-158.152 380.5,-148.658 380.5,-139.725\" fill=\"none\" stroke=\"black\"/>\n",
       "<polygon fill=\"black\" points=\"384,-139.607 380.5,-129.607 377,-139.607 384,-139.607\" stroke=\"black\"/>\n",
       "</g>\n",
       "<!-- 139805282488272 -->\n",
       "<g class=\"node\" id=\"node28\"><title>139805282488272</title>\n",
       "<polygon fill=\"none\" points=\"196.5,-0.5 196.5,-46.5 564.5,-46.5 564.5,-0.5 196.5,-0.5\" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"298\" y=\"-19.8\">time_distributed: TimeDistributed</text>\n",
       "<polyline fill=\"none\" points=\"399.5,-0.5 399.5,-46.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"427\" y=\"-31.3\">input:</text>\n",
       "<polyline fill=\"none\" points=\"399.5,-23.5 454.5,-23.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"427\" y=\"-8.3\">output:</text>\n",
       "<polyline fill=\"none\" points=\"454.5,-0.5 454.5,-46.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"509.5\" y=\"-31.3\">(None, 200, 800)</text>\n",
       "<polyline fill=\"none\" points=\"454.5,-23.5 564.5,-23.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"509.5\" y=\"-8.3\">(None, 200, 1)</text>\n",
       "</g>\n",
       "<!-- 139805288210616&#45;&gt;139805282488272 -->\n",
       "<g class=\"edge\" id=\"edge30\"><title>139805288210616-&gt;139805282488272</title>\n",
       "<path d=\"M380.5,-83.3664C380.5,-75.1516 380.5,-65.6579 380.5,-56.7252\" fill=\"none\" stroke=\"black\"/>\n",
       "<polygon fill=\"black\" points=\"384,-56.6068 380.5,-46.6068 377,-56.6069 384,-56.6068\" stroke=\"black\"/>\n",
       "</g>\n",
       "</g>\n",
       "</svg>"
      ],
      "text/plain": [
       "<IPython.core.display.SVG object>"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "SVG(model_to_dot(model, show_shapes=True, show_layer_names=True).create(prog='dot', format='svg'))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Load modular pretrained weights"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "scrolled": false
   },
   "outputs": [],
   "source": [
    "model.load_weights('../models/cfo_nn_tanh_omgega.hdf5', by_name=True)\n",
    "model.load_weights('../models/equalization3.hdf5', by_name=True)\n",
    "model.load_weights('BiGRU_BSC.hdf5', by_name=True)\n",
    "model.compile(tf.keras.optimizers.Adam(0.00001),'mse')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "# history = model.fit_generator(\n",
    "#     generator=training_generator,\n",
    "#     steps_per_epoch=100,\n",
    "#     epochs=5)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Evaluate with no CFO Effect"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Params:\n",
    "    SNR_RANGE = [0.0, 3.0, 5.0, 7.0, 10.0, 12.0, 15.0, 17.0, 20.0, 25.0, 40.0]\n",
    "    NUM_SAMPLES = 1000"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "SNR_dB = 0.000000\n",
      "\t[Modular]  Ber = 0.44401000 | Bler =1.00000000 - 2.27253s\n",
      "\t[Baseline] Ber = 0.31358500 | Bler =1.00000000 - 8.45572s\n",
      "SNR_dB = 3.000000\n",
      "\t[Modular]  Ber = 0.31696000 | Bler =1.00000000 - 1.52915s\n",
      "\t[Baseline] Ber = 0.14105500 | Bler =1.00000000 - 8.05099s\n",
      "SNR_dB = 5.000000\n",
      "\t[Modular]  Ber = 0.16489000 | Bler =0.99900000 - 1.49146s\n",
      "\t[Baseline] Ber = 0.05989500 | Bler =0.91600000 - 7.96034s\n",
      "SNR_dB = 7.000000\n",
      "\t[Modular]  Ber = 0.04633500 | Bler =0.92600000 - 1.49234s\n",
      "\t[Baseline] Ber = 0.02369500 | Bler =0.70000000 - 7.95117s\n",
      "SNR_dB = 10.000000\n",
      "\t[Modular]  Ber = 0.00294500 | Bler =0.30500000 - 1.50932s\n",
      "\t[Baseline] Ber = 0.00751000 | Bler =0.46300000 - 7.96546s\n",
      "SNR_dB = 12.000000\n",
      "\t[Modular]  Ber = 0.00063000 | Bler =0.10000000 - 1.50965s\n",
      "\t[Baseline] Ber = 0.00468000 | Bler =0.37000000 - 8.06240s\n",
      "SNR_dB = 15.000000\n",
      "\t[Modular]  Ber = 0.00023500 | Bler =0.04700000 - 1.51959s\n",
      "\t[Baseline] Ber = 0.00315000 | Bler =0.31000000 - 8.06869s\n",
      "SNR_dB = 17.000000\n",
      "\t[Modular]  Ber = 0.00020000 | Bler =0.04000000 - 1.48269s\n",
      "\t[Baseline] Ber = 0.00252500 | Bler =0.28100000 - 8.17000s\n",
      "SNR_dB = 20.000000\n",
      "\t[Modular]  Ber = 0.00017000 | Bler =0.03400000 - 1.51823s\n",
      "\t[Baseline] Ber = 0.00196500 | Bler =0.26100000 - 7.95117s\n",
      "SNR_dB = 25.000000\n",
      "\t[Modular]  Ber = 0.00016000 | Bler =0.03200000 - 1.50616s\n",
      "\t[Baseline] Ber = 0.00172000 | Bler =0.24500000 - 8.05687s\n",
      "SNR_dB = 40.000000\n",
      "\t[Modular]  Ber = 0.00014500 | Bler =0.02900000 - 1.51443s\n",
      "\t[Baseline] Ber = 0.00166500 | Bler =0.23500000 - 8.06746s\n"
     ]
    }
   ],
   "source": [
    "baseline = Baseline()\n",
    "\n",
    "model = tf.keras.models.load_model('../models/end2end_receiver.hdf5',\n",
    "                                   custom_objects={'PREAMBLE_LEN': PREAMBLE_LEN, 'tf': tf}, compile=False)\n",
    "                                   \n",
    "                                   \n",
    "ber_logs, bler_logs = [], []\n",
    "for i, snr in enumerate(Params.SNR_RANGE):\n",
    "    print('SNR_dB = %f' % snr)\n",
    "    x_test, y_test = next(radio.end2end_data_generator(1/10000, \n",
    "                                                       snr, \n",
    "                                                       batch_size=Params.NUM_SAMPLES, \n",
    "                                                       num_cpus=16, \n",
    "                                                       seed=2019))\n",
    "    Y_test = np.squeeze(y_test[0], -1)  #(batch, 100, 1) to (batch, 100)\n",
    "    \n",
    "    # Run Baseline/Neral Receiver\n",
    "    t1 = time.time()\n",
    "        \n",
    "    [preambles, corrupted_packets] = x_test\n",
    "    preambles = preambles.view(complex)\n",
    "    convolved_preamble = np.array(corrupted_packets[:, :radio.preamble_len, :]).view(complex)\n",
    "    convolved_data     = np.array(corrupted_packets[:, radio.preamble_len:, :]).view(complex)\n",
    "    \n",
    "    with mp.Pool(mp.cpu_count()) as pool:\n",
    "        baseline_estimate = pool.starmap(baseline, [(i, j , k) for i, j, k \n",
    "                                                    in zip(convolved_data, convolved_preamble, preambles)])\n",
    "\n",
    "    t2 = time.time()\n",
    "    \n",
    "    predictions, _, _ = model.predict(x_test, 500)\n",
    "    nn_estimate = np.squeeze(predictions, -1).round()\n",
    "    t3 = time.time()\n",
    "    \n",
    "    # Measure BER / BKER for two receivers\n",
    "    ber, bler       = get_ber_bler(np.array(baseline_estimate), Y_test)\n",
    "    nn_ber, nn_bler = get_ber_bler(nn_estimate, Y_test)\n",
    "\n",
    "    print('\\t[Modular]  Ber = {:.8f} | Bler ={:.8f} - {:3.5f}s'.format(nn_ber, nn_bler, t3 - t2))\n",
    "    print('\\t[Baseline] Ber = {:.8f} | Bler ={:.8f} - {:3.5f}s'.format(ber, bler, t2-t1))\n",
    "    \n",
    "    ber_logs.append([ber, nn_ber])\n",
    "    bler_logs.append([bler, nn_bler])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": [
    "np.savetxt('ber_nocfo.txt', ber_logs)\n",
    "np.savetxt('bler_nocfo.txt', bler_logs)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Evaluate with CFO Effect"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "SNR_dB = 0.000000\n",
      "\t[Modular]  Ber = 0.44527500 | Bler =1.00000000 - 1.50157s\n",
      "\t[Baseline] Ber = 0.47016500 | Bler =1.00000000 - 7.96942s\n",
      "SNR_dB = 3.000000\n",
      "\t[Modular]  Ber = 0.31816000 | Bler =1.00000000 - 1.51601s\n",
      "\t[Baseline] Ber = 0.43339000 | Bler =1.00000000 - 7.93774s\n",
      "SNR_dB = 5.000000\n",
      "\t[Modular]  Ber = 0.17108500 | Bler =0.99900000 - 1.49772s\n",
      "\t[Baseline] Ber = 0.40966000 | Bler =0.99400000 - 7.94886s\n",
      "SNR_dB = 7.000000\n",
      "\t[Modular]  Ber = 0.05089000 | Bler =0.92800000 - 1.49409s\n",
      "\t[Baseline] Ber = 0.39582500 | Bler =0.96800000 - 8.05085s\n",
      "SNR_dB = 10.000000\n",
      "\t[Modular]  Ber = 0.00345000 | Bler =0.31600000 - 1.51068s\n",
      "\t[Baseline] Ber = 0.38490000 | Bler =0.93600000 - 7.96551s\n",
      "SNR_dB = 12.000000\n",
      "\t[Modular]  Ber = 0.00072000 | Bler =0.10400000 - 1.51596s\n",
      "\t[Baseline] Ber = 0.37955500 | Bler =0.91900000 - 7.96857s\n",
      "SNR_dB = 15.000000\n",
      "\t[Modular]  Ber = 0.00023000 | Bler =0.04600000 - 1.72547s\n",
      "\t[Baseline] Ber = 0.37455000 | Bler =0.90400000 - 8.26109s\n",
      "SNR_dB = 17.000000\n",
      "\t[Modular]  Ber = 0.00017500 | Bler =0.03500000 - 1.53035s\n",
      "\t[Baseline] Ber = 0.37374000 | Bler =0.89900000 - 8.42508s\n",
      "SNR_dB = 20.000000\n",
      "\t[Modular]  Ber = 0.00013500 | Bler =0.02700000 - 1.52658s\n",
      "\t[Baseline] Ber = 0.37242000 | Bler =0.89700000 - 8.08949s\n",
      "SNR_dB = 25.000000\n",
      "\t[Modular]  Ber = 0.00014000 | Bler =0.02800000 - 1.50016s\n",
      "\t[Baseline] Ber = 0.37007000 | Bler =0.89100000 - 7.96501s\n",
      "SNR_dB = 40.000000\n",
      "\t[Modular]  Ber = 0.00012500 | Bler =0.02500000 - 1.50231s\n",
      "\t[Baseline] Ber = 0.36899000 | Bler =0.89800000 - 7.95622s\n"
     ]
    }
   ],
   "source": [
    "ber_logs, bler_logs = [], []\n",
    "for i, snr in enumerate(Params.SNR_RANGE):\n",
    "    print('SNR_dB = %f' % snr)\n",
    "    x_test, y_test = next(radio.end2end_data_generator(OMEGA_TRAIN, \n",
    "                                                       snr, \n",
    "                                                       batch_size=Params.NUM_SAMPLES, \n",
    "                                                       num_cpus=16, \n",
    "                                                       seed=2019))\n",
    "    Y_test = np.squeeze(y_test[0], -1)  #(batch, 100, 1) to (batch, 100)\n",
    "    \n",
    "    # Run Baseline/Neral Receiver\n",
    "    t1 = time.time()\n",
    "        \n",
    "    [preambles, corrupted_packets] = x_test\n",
    "    preambles = preambles.view(complex)\n",
    "    convolved_preamble = np.array(corrupted_packets[:, :radio.preamble_len, :]).view(complex)\n",
    "    convolved_data     = np.array(corrupted_packets[:, radio.preamble_len:, :]).view(complex)\n",
    "    \n",
    "    with mp.Pool(mp.cpu_count()) as pool:\n",
    "        baseline_estimate = pool.starmap(baseline, [(i, j , k) for i, j, k \n",
    "                                                    in zip(convolved_data, convolved_preamble, preambles)])\n",
    "\n",
    "    t2 = time.time()\n",
    "    \n",
    "    predictions, _, _ = model.predict(x_test, 500)\n",
    "    nn_estimate = np.squeeze(predictions, -1).round()\n",
    "    t3 = time.time()\n",
    "    \n",
    "    # Measure BER / BKER for two receivers\n",
    "    ber, bler       = get_ber_bler(np.array(baseline_estimate), Y_test)\n",
    "    nn_ber, nn_bler = get_ber_bler(nn_estimate, Y_test)\n",
    "\n",
    "    print('\\t[Modular]  Ber = {:.8f} | Bler ={:.8f} - {:3.5f}s'.format(nn_ber, nn_bler, t3 - t2))\n",
    "    print('\\t[Baseline] Ber = {:.8f} | Bler ={:.8f} - {:3.5f}s'.format(ber, bler, t2-t1))\n",
    "    \n",
    "    ber_logs.append([ber, nn_ber])\n",
    "    bler_logs.append([bler, nn_bler])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [],
   "source": [
    "np.savetxt('ber_withcfo.txt', ber_logs)\n",
    "np.savetxt('bler_withcfo.txt', bler_logs)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "deepcom",
   "language": "python",
   "name": "deepcom"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
